Popular Searches
Popular Course Categories
Popular Courses

Using Named Queries and Criteria Queries

Using Named Queries and Criteria Queries

Hibernate Relationships and Query Language

Using Named Queries and Criteria Queries in Hibernate

Course: Advanced Java Training

Topic: Using Named Queries and Criteria Queries

Course Link: Advanced Java Training Course

Demo Link: Register for a Course Demo

Website: JustAcademy Official Website


1. Introduction

Hibernate is an Object-Relational Mapping (ORM) framework that allows Java applications to interact with relational databases using Java objects. It provides several query mechanisms for retrieving, filtering, updating, and deleting persistent data.

Named queries are reusable queries identified by a name. They are commonly declared using annotations such as @NamedQuery and can use Java Persistence Query Language (JPQL) or native SQL, depending on the query annotation.

Criteria queries are constructed programmatically using Java API objects rather than writing the entire query as a string. They are particularly useful when search conditions depend on user input or optional filters.

Both techniques are useful in Advanced Java applications that use Hibernate or Jakarta Persistence. The examples in these notes use jakarta.persistence, which is appropriate for modern Jakarta Persistence applications. Older projects may use javax.persistence, depending on their framework versions.

2. Learning Objectives

  • Understand named queries and Criteria queries.
  • Learn the difference between JPQL and SQL.
  • Create and execute reusable named queries.
  • Pass parameters safely to queries.
  • Construct Criteria queries using CriteriaBuilder and CriteriaQuery.
  • Apply filtering, sorting, pagination, and aggregate functions.
  • Build dynamic queries using optional search conditions.
  • Retrieve entities, individual attributes, and DTO projections.
  • Use joins to query related entities.
  • Understand query errors, performance considerations, and best practices.

3. Understanding JPQL

JPQL stands for Java Persistence Query Language. It queries entities and their mapped Java attributes instead of directly referencing physical database tables and columns. The persistence provider translates JPQL into SQL appropriate for the configured database.

Example Entity

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.math.BigDecimal;

@Entity
@Table(name = "employees")
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "employee_name", nullable = false)
    private String name;

    @Column(nullable = false)
    private String department;

    @Column(nullable = false)
    private BigDecimal salary;

    public Employee() {
    }

    public Employee(String name, String department, BigDecimal salary) {
        this.name = name;
        this.department = department;
        this.salary = salary;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getDepartment() {
        return department;
    }

    public BigDecimal getSalary() {
        return salary;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }
}

JPQL Query Example

SELECT e
FROM Employee e
WHERE e.department = :department

This query retrieves employees whose department matches the supplied parameter. It references the entity name Employee and the Java attribute department, rather than the physical table and column names.

4. What Is a Named Query?

A named query is a reusable query defined with a unique name. Application code can execute the query by referencing that name instead of repeating the query text in multiple methods.

In Jakarta Persistence, named JPQL queries are commonly declared using @NamedQuery on an entity class. Multiple queries can be grouped using @NamedQueries.

Advantages of Named Queries

  • Reusability: The same query can be used by multiple application components.
  • Centralized definitions: Query logic can be maintained in a common location.
  • Readability: Meaningful query names describe the purpose of each operation.
  • Maintainability: A shared query can be modified without changing every method that uses it.
  • Validation: Providers may validate named queries during initialization, depending on their configuration and implementation.

5. Creating a Named Query Using @NamedQuery

The @NamedQuery annotation defines a reusable JPQL query. Its name attribute identifies the query, while the query attribute contains the JPQL statement.

Example

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.Table;

@Entity
@Table(name = "employees")
@NamedQuery(
    name = "Employee.findAll",
    query = "SELECT e FROM Employee e"
)
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String department;

    public Employee() {
    }
}

The query is registered under the name Employee.findAll. This name is used when executing the query through the EntityManager.

6. Defining Multiple Named Queries

The @NamedQueries annotation allows multiple named queries to be declared together. This is useful when an entity supports several common search and reporting operations.

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.NamedQueries;
import jakarta.persistence.NamedQuery;
import java.math.BigDecimal;

@Entity
@NamedQueries({
    @NamedQuery(
        name = "Employee.findAll",
        query = "SELECT e FROM Employee e"
    ),
    @NamedQuery(
        name = "Employee.findByDepartment",
        query = "SELECT e FROM Employee e " +
                "WHERE e.department = :department"
    ),
    @NamedQuery(
        name = "Employee.findByMinimumSalary",
        query = "SELECT e FROM Employee e " +
                "WHERE e.salary >= :minimumSalary"
    ),
    @NamedQuery(
        name = "Employee.countAll",
        query = "SELECT COUNT(e) FROM Employee e"
    )
})
public class Employee {

    @Id
    private Long id;

    private String name;
    private String department;
    private BigDecimal salary;

    public Employee() {
    }
}

Each query name must be unique within the persistence unit. The query's entity name and attribute names must match the mapped Java model.

7. Executing Named Queries

The EntityManager.createNamedQuery() method executes a previously registered named query. When the expected result type is known, use the typed form to improve type safety.

Example: Retrieve All Employees

import jakarta.persistence.EntityManager;
import java.util.List;

public class EmployeeRepository {

    private final EntityManager entityManager;

    public EmployeeRepository(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    public List<Employee> findAll() {
        return entityManager
            .createNamedQuery("Employee.findAll", Employee.class)
            .getResultList();
    }
}

The method executes the named query and returns the matching Employee entities. The EntityManager must be configured and managed correctly by the application.

8. Using Parameters in Named Queries

Parameters allow values to be supplied when a query executes. They prevent the need to concatenate user input into the query string and are an important defense against query injection.

Named Query Definition

@NamedQuery(
    name = "Employee.findByDepartment",
    query = "SELECT e FROM Employee e " +
            "WHERE e.department = :department"
)

Executing the Query

public List<Employee> findByDepartment(String department) {
    return entityManager
        .createNamedQuery("Employee.findByDepartment", Employee.class)
        .setParameter("department", department)
        .getResultList();
}

The parameter name in setParameter() must match the parameter name in the JPQL query. Use bound parameters rather than concatenating values supplied by users.

9. Retrieving a Single Result

The getSingleResult() method is useful when a query is expected to return exactly one result, such as an aggregate count or a record identified by a unique value.

@NamedQuery(
    name = "Employee.countAll",
    query = "SELECT COUNT(e) FROM Employee e"
)
public Long countEmployees() {
    return entityManager
        .createNamedQuery("Employee.countAll", Long.class)
        .getSingleResult();
}

getSingleResult() can throw NoResultException when no result exists and NonUniqueResultException when multiple results are returned. Aggregate count queries normally return a single result, including zero when there are no matching records.

10. Updating and Deleting Records with Named Queries

Named JPQL queries can perform bulk updates and deletes. Unlike SELECT queries, these operations use executeUpdate() and should be executed within an appropriate transaction.

Bulk Update

@NamedQuery(
    name = "Employee.updateDepartment",
    query = "UPDATE Employee e " +
            "SET e.department = :newDepartment " +
            "WHERE e.department = :oldDepartment"
)
public int updateDepartment(
        EntityManager entityManager,
        String oldDepartment,
        String newDepartment) {

    return entityManager
        .createNamedQuery("Employee.updateDepartment")
        .setParameter("oldDepartment", oldDepartment)
        .setParameter("newDepartment", newDepartment)
        .executeUpdate();
}

Bulk Delete

@NamedQuery(
    name = "Employee.deleteByDepartment",
    query = "DELETE FROM Employee e " +
            "WHERE e.department = :department"
)
public int deleteByDepartment(
        EntityManager entityManager,
        String department) {

    return entityManager
        .createNamedQuery("Employee.deleteByDepartment")
        .setParameter("department", department)
        .executeUpdate();
}

Important: Bulk updates and deletes bypass normal per-entity dirty checking. Managed objects may therefore contain stale values after execution. Flush pending changes when necessary, and clear or refresh affected entities to avoid inconsistent in-memory state.

11. What Is a Criteria Query?

A Criteria query is a query built programmatically through the Jakarta Persistence Criteria API. Instead of expressing the entire query as a JPQL string, the developer constructs query objects, roots, predicates, expressions, and ordering rules.

Criteria queries are particularly useful when a search form offers optional filters. For example, a user may search for employees by department, minimum salary, name, or several conditions together.

Important Criteria API Components

Component Purpose
CriteriaBuilder Creates query expressions, predicates, and ordering rules.
CriteriaQuery Represents the structure of a SELECT query.
Root Represents the entity used as the query root.
Predicate Represents a filtering condition.
Path Represents navigation to an entity attribute.
Join Represents a join between related entities.
Order Represents ascending or descending sorting.
TypedQuery Executes a query with a declared result type.

12. Creating a Basic Criteria Query

A basic Criteria query follows these steps:

  1. Obtain a CriteriaBuilder from the EntityManager.
  2. Create a typed CriteriaQuery.
  3. Define the query root.
  4. Specify the selected entity or expression.
  5. Apply conditions if needed.
  6. Execute the query and retrieve results.

Example: Retrieve All Employees

import jakarta.persistence.EntityManager;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import java.util.List;

public List<Employee> findAllEmployees(
        EntityManager entityManager) {

    CriteriaBuilder cb = entityManager.getCriteriaBuilder();

    CriteriaQuery<Employee> cq =
        cb.createQuery(Employee.class);

    Root<Employee> employee =
        cq.from(Employee.class);

    cq.select(employee);

    return entityManager.createQuery(cq).getResultList();
}

The CriteriaBuilder creates the query structure, CriteriaQuery defines the expected result, and Root represents the Employee entity. The final query returns a list of matching employee objects.

13. Filtering Results with Criteria Queries

The where() method restricts query results. Conditions are built using methods such as equal(), notEqual(), greaterThan(), lessThan(), like(), and between().

Example: Search by Department

public List<Employee> findByDepartment(
        EntityManager entityManager,
        String departmentName) {

    CriteriaBuilder cb = entityManager.getCriteriaBuilder();

    CriteriaQuery<Employee> cq =
        cb.createQuery(Employee.class);

    Root<Employee> employee =
        cq.from(Employee.class);

    cq.select(employee)
      .where(
          cb.equal(
              employee.get("department"),
              departmentName
          )
      );

    return entityManager.createQuery(cq).getResultList();
}

This query returns employees whose department matches the supplied department name.

Common Criteria Conditions

Method Purpose Example
equal() Checks equality. cb.equal(root.get("department"), "IT")
notEqual() Checks inequality. cb.notEqual(root.get("department"), "HR")
greaterThan() Checks whether a comparable value is greater. cb.greaterThan(root.get("salary"), minimum)
lessThan() Checks whether a comparable value is smaller. cb.lessThan(root.get("salary"), maximum)
between() Checks whether a value falls within a range. cb.between(root.get("salary"), low, high)
like() Matches a string pattern. cb.like(root.get("name"), "A%")
isNull() Checks whether an attribute is null. cb.isNull(root.get("manager"))
isNotNull() Checks whether an attribute is not null. cb.isNotNull(root.get("email"))

14. Combining Multiple Conditions

Criteria queries can combine predicates using and(), or(), and not(). These methods allow complex search conditions to be built programmatically.

Example: Department and Minimum Salary

import java.math.BigDecimal;

public List<Employee> findByDepartmentAndSalary(
        EntityManager entityManager,
        String departmentName,
        BigDecimal minimumSalary) {

    CriteriaBuilder cb = entityManager.getCriteriaBuilder();

    CriteriaQuery<Employee> cq =
        cb.createQuery(Employee.class);

    Root<Employee> employee =
        cq.from(Employee.class);

    cq.select(employee).where(
        cb.and(
            cb.equal(
                employee.get("department"),
                departmentName
            ),
            cb.greaterThanOrEqualTo(
                employee.get("salary"),
                minimumSalary
            )
        )
    );

    return entityManager.createQuery(cq).getResultList();
}

The query retrieves employees who belong to the specified department and whose salary is at least the supplied minimum amount.

Example: OR Condition

cq.where(
    cb.or(
        cb.equal(employee.get("department"), "IT"),
        cb.equal(employee.get("department"), "Finance")
    )
);

This condition retrieves employees who belong to either the IT department or the Finance department.

15. Building Dynamic Criteria Queries

Dynamic queries are useful when a search screen allows users to provide different combinations of filters. The application can add predicates only when corresponding values are supplied.

Example: Optional Search Filters

import jakarta.persistence.EntityManager;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public List<Employee> searchEmployees(
        EntityManager entityManager,
        String department,
        BigDecimal minimumSalary,
        String nameKeyword) {

    CriteriaBuilder cb = entityManager.getCriteriaBuilder();

    CriteriaQuery<Employee> cq =
        cb.createQuery(Employee.class);

    Root<Employee> employee =
        cq.from(Employee.class);

    List<Predicate> predicates = new ArrayList<>();

    if (department != null && !department.isBlank()) {
        predicates.add(
            cb.equal(employee.get("department"), department)
        );
    }

    if (minimumSalary != null) {
        predicates.add(
            cb.greaterThanOrEqualTo(
                employee.get("salary"),
                minimumSalary
            )
        );
    }

    if (nameKeyword != null && !nameKeyword.isBlank()) {
        predicates.add(
            cb.like(
                cb.lower(employee.get("name")),
                "%" + nameKeyword.toLowerCase() + "%"
            )
        );
    }

    cq.select(employee)
      .where(predicates.toArray(new Predicate[0]));

    return entityManager.createQuery(cq).getResultList();
}

Explanation

  1. The method accepts three optional search parameters.
  2. A CriteriaBuilder and CriteriaQuery are created.
  3. A root is defined for the Employee entity.
  4. A predicate is added only when its corresponding filter has a value.
  5. The predicates are applied to the query using where().
  6. The query executes and returns the matching employee records.

Multiple predicates passed to where() are combined with AND semantics. If no predicates are added, this example returns all employees. Consider applying pagination or other restrictions when the dataset may be large.

16. Sorting Criteria Query Results

Use CriteriaBuilder.asc() for ascending order and CriteriaBuilder.desc() for descending order. The orderBy() method applies the sorting rules.

Sort Employees by Name

CriteriaBuilder cb = entityManager.getCriteriaBuilder();

CriteriaQuery<Employee> cq =
    cb.createQuery(Employee.class);

Root<Employee> employee =
    cq.from(Employee.class);

cq.select(employee)
  .orderBy(cb.asc(employee.get("name")));

List<Employee> employees =
    entityManager.createQuery(cq).getResultList();

Sort by Salary in Descending Order

cq.orderBy(cb.desc(employee.get("salary")));

Sort by Multiple Attributes

cq.orderBy(
    cb.asc(employee.get("department")),
    cb.desc(employee.get("salary"))
);

This sorts employees by department and then by salary within each department.

17. Pagination with Criteria Queries

Pagination limits the number of results returned at a time. It is commonly used in employee directories, product listings, and administrative dashboards.

public List<Employee> findEmployeePage(
        EntityManager entityManager,
        int pageNumber,
        int pageSize) {

    if (pageNumber < 0 || pageSize < 1) {
        throw new IllegalArgumentException(
            "Page number must be non-negative and page size must be positive."
        );
    }

    CriteriaBuilder cb = entityManager.getCriteriaBuilder();

    CriteriaQuery<Employee> cq =
        cb.createQuery(Employee.class);

    Root<Employee> employee =
        cq.from(Employee.class);

    cq.select(employee)
      .orderBy(cb.asc(employee.get("id")));

    return entityManager.createQuery(cq)
        .setFirstResult(pageNumber * pageSize)
        .setMaxResults(pageSize)
        .getResultList();
}

setFirstResult() defines the zero-based starting position, while setMaxResults() limits the number of returned rows. Deterministic ordering is important for consistent page boundaries.

18. Selecting Specific Fields and DTO Projections

Sometimes an application needs only selected attributes rather than complete entity objects. Criteria queries support selecting individual fields or constructing Data Transfer Objects (DTOs).

Select Employee Names

CriteriaBuilder cb = entityManager.getCriteriaBuilder();

CriteriaQuery<String> cq =
    cb.createQuery(String.class);

Root<Employee> employee =
    cq.from(Employee.class);

cq.select(employee.get("name"))
  .orderBy(cb.asc(employee.get("name")));

List<String> names =
    entityManager.createQuery(cq).getResultList();

DTO Projection Example

public class EmployeeSummary {

    private final String name;
    private final String department;

    public EmployeeSummary(String name, String department) {
        this.name = name;
        this.department = department;
    }

    public String getName() {
        return name;
    }

    public String getDepartment() {
        return department;
    }
}
CriteriaBuilder cb = entityManager.getCriteriaBuilder();

CriteriaQuery<EmployeeSummary> cq =
    cb.createQuery(EmployeeSummary.class);

Root<Employee> employee =
    cq.from(Employee.class);

cq.select(
    cb.construct(
        EmployeeSummary.class,
        employee.get("name"),
        employee.get("department")
    )
);

List<EmployeeSummary> summaries =
    entityManager.createQuery(cq).getResultList();

The constructor projection creates one DTO for each result row. The DTO constructor must accept compatible argument types in the selected order. DTO projections are useful for reports and read-only screens that do not require managed entity objects.

19. Aggregate Functions in Criteria Queries

Aggregate functions summarize data across multiple rows. The Criteria API supports operations such as counting records, calculating averages, and finding minimum or maximum values.

Count All Employees

CriteriaBuilder cb = entityManager.getCriteriaBuilder();

CriteriaQuery<Long> cq =
    cb.createQuery(Long.class);

Root<Employee> employee =
    cq.from(Employee.class);

cq.select(cb.count(employee));

Long totalEmployees =
    entityManager.createQuery(cq).getSingleResult();

Calculate Average Salary

CriteriaBuilder cb = entityManager.getCriteriaBuilder();

CriteriaQuery<Double> cq =
    cb.createQuery(Double.class);

Root<Employee> employee =
    cq.from(Employee.class);

cq.select(cb.avg(employee.get("salary")));

Double averageSalary =
    entityManager.createQuery(cq).getSingleResult();

Aggregate operations have defined result types. Ensure the selected Java type is compatible with the operation. For financial calculations, also consider the precision and rounding requirements of the application.

Group Employees by Department

CriteriaBuilder cb = entityManager.getCriteriaBuilder();

CriteriaQuery<Object[]> cq =
    cb.createQuery(Object[].class);

Root<Employee> employee =
    cq.from(Employee.class);

cq.multiselect(
    employee.get("department"),
    cb.count(employee)
);

cq.groupBy(employee.get("department"));

List<Object[]> results =
    entityManager.createQuery(cq).getResultList();

Each result contains a department value and the number of employees in that department. A typed DTO projection can make grouped report results easier to maintain than using arrays of objects.

20. Using Joins in Criteria Queries

Joins allow queries to navigate mapped relationships between entities. Suppose each Employee has a many-to-one relationship with a Department.

@ManyToOne
@JoinColumn(name = "department_id")
private Department department;

Find Employees by Department Name

import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType;

CriteriaBuilder cb = entityManager.getCriteriaBuilder();

CriteriaQuery<Employee> cq =
    cb.createQuery(Employee.class);

Root<Employee> employee =
    cq.from(Employee.class);

Join<Employee, Department> department =
    employee.join("department", JoinType.INNER);

cq.select(employee)
  .where(
      cb.equal(department.get("departmentName"), "IT")
  );

List<Employee> results =
    entityManager.createQuery(cq).getResultList();

Common Join Types

Join Type Description
INNER Returns records that have matching related records.
LEFT Retains records from the left side even when no matching related record exists.
RIGHT Retains records from the right side where supported and appropriate.

Joins can produce repeated root entities when multiple related rows match. Review the query semantics and use distinct results where appropriate.

21. Using DISTINCT in Criteria Queries

The distinct() method requests distinct query results. It can be useful when joining an entity to a collection causes repeated root entity results.

CriteriaBuilder cb = entityManager.getCriteriaBuilder();

CriteriaQuery<Employee> cq =
    cb.createQuery(Employee.class);

Root<Employee> employee =
    cq.from(Employee.class);

employee.join("projects", JoinType.INNER);

cq.select(employee).distinct(true);

List<Employee> results =
    entityManager.createQuery(cq).getResultList();

This example assumes that Employee has a mapped collection association named projects. Ensure that the relationship exists in the entity model before using the query.

22. Named Queries vs. Criteria Queries

Feature Named Queries Criteria Queries
Query definition Usually declared as a named JPQL query. Constructed using Java API objects.
Best use case Stable, reusable queries. Dynamic search and filtering.
Readability Often concise and easy to understand. Can become verbose for complex queries.
Dynamic conditions Possible, but optional query structures can be less convenient. Conditions can be added or omitted programmatically.
Type safety JPQL entity and attribute names are written as strings. Typed results are supported; the static metamodel can improve attribute type checking.
Reusability Queries are referenced through registered names. Query-building logic can be reused in methods and repository components.
Maintenance Suitable for centralized, frequently used queries. Suitable for flexible query construction.

Neither approach is always better. Choose based on whether the query structure is stable or needs to change at runtime.

23. Common Errors and Troubleshooting

Error Possible Cause Solution
Named query not found. The name is incorrect or the query is not registered. Verify the annotation name and persistence configuration.
JPQL syntax error. Incorrect entity name, attribute name, or query syntax. Check the entity mapping and JPQL statement.
Parameter not bound. A query parameter is missing or incorrectly named. Match each parameter with the correct setParameter() call.
Criteria attribute error. A string-based attribute path does not exist. Verify the Java attribute name or use the static metamodel.
Unexpected query results. Incorrect predicates or join conditions. Review the query conditions and association mappings.
Duplicate results. A join produces multiple matching rows. Review join semantics and consider distinct(true).
Query performance is poor. Large result sets, missing indexes, or inefficient joins. Use pagination, inspect SQL, and analyze database execution plans.
Bulk update leaves stale entities. Managed objects are not synchronized with bulk changes. Flush where necessary and clear or refresh affected entities.
Native query fails. Incorrect physical table or column names. Validate the SQL against the target database schema.

24. Best Practices

  • Use named queries for stable and frequently reused query logic.
  • Give named queries descriptive names such as Employee.findByDepartment.
  • Use typed query APIs when the expected result type is known.
  • Bind values through query parameters instead of concatenating user input.
  • Use Criteria queries when optional search conditions must be added dynamically.
  • Consider the Jakarta Persistence static metamodel for stronger type checking.
  • Use projections when the application needs only a subset of entity attributes.
  • Apply pagination to queries that may return many records.
  • Use deterministic ordering for paginated results.
  • Review joins and fetching behavior to avoid unnecessary queries.
  • Execute database-changing operations within appropriate transaction boundaries.
  • Handle empty results and exceptions according to the application's requirements.
  • Inspect generated SQL and database execution plans when diagnosing performance issues.
  • Test queries with realistic data and a representative database environment.

25. Practical Exercise

Project: Employee Search and Reporting Module

Create an application that manages employee information and supports searching and reporting through named and Criteria queries.

Requirements

  1. Create an Employee entity with ID, name, department, and salary attributes.
  2. Define a named query to retrieve all employees.
  3. Create a named query to retrieve employees by department.
  4. Create a named query to count employees.
  5. Build a Criteria query with optional department, name, and minimum salary filters.
  6. Sort results by employee name or salary.
  7. Add pagination to the search results.
  8. Create a report that counts employees by department.
  9. Handle empty results and invalid search parameters.
  10. Compare the readability and maintainability of both query approaches.

Expected Outcome

After completing this exercise, you should be able to create reusable named queries, construct dynamic Criteria queries, safely bind parameters, and retrieve filtered and aggregated database results.

26. Interview Questions and Answers

Q1. What is a named query in Hibernate?

Answer: A named query is a reusable query registered under a unique name, commonly using the @NamedQuery annotation.

Q2. What is JPQL?

Answer: JPQL is a query language that operates on entities and their mapped Java attributes rather than directly querying physical database tables.

Q3. How do you execute a named query?

Answer: Use EntityManager.createNamedQuery(), set any required parameters, and retrieve the results or execute the update.

Q4. What is a Criteria query?

Answer: It is a query constructed programmatically through the Jakarta Persistence Criteria API.

Q5. What is CriteriaBuilder?

Answer: CriteriaBuilder creates query objects, predicates, comparisons, aggregate expressions, and sorting rules.

Q6. What is the purpose of Root?

Answer: Root represents the entity used as the starting point of a Criteria query.

Q7. How do you combine Criteria conditions?

Answer: Use CriteriaBuilder.and() and CriteriaBuilder.or() to combine predicates according to the required logic.

Q8. When should Criteria queries be used?

Answer: They are useful when query conditions depend on runtime input, such as optional filters in search forms.

Q9. Can named queries perform updates and deletes?

Answer: Yes. Named JPQL update and delete queries can be executed with executeUpdate() inside an appropriate transaction.

Q10. What is the difference between getResultList() and getSingleResult()?

Answer: getResultList() returns a list that may be empty, whereas getSingleResult() expects exactly one result and may throw an exception otherwise.

Q11. How can query injection be prevented?

Answer: Use parameter binding for input values and avoid concatenating untrusted values into query strings.

Q12. What is a static metamodel?

Answer: It is a set of generated classes representing persistent attributes, allowing stronger compile-time checking when constructing Criteria queries.

Q13. What does distinct(true) do?

Answer: It requests distinct query results, which can help when joins produce repeated root entities.

Q14. What is a named native query?

Answer: It is a reusable SQL query declared using @NamedNativeQuery rather than JPQL.

Q15. What should be considered after a bulk update?

Answer: Bulk operations can leave managed entity instances stale. Flush pending changes when necessary and clear or refresh affected objects as appropriate.

27. Quick Revision Checklist

  • @NamedQuery defines a reusable JPQL query.
  • @NamedQueries groups multiple named queries.
  • createNamedQuery() retrieves a registered query for execution.
  • setParameter() binds query values safely.
  • CriteriaBuilder constructs expressions and predicates.
  • CriteriaQuery defines the query structure.
  • Root identifies the query's entity root.
  • where() applies filtering conditions.
  • orderBy() defines result ordering.
  • setFirstResult() and setMaxResults() support pagination.
  • Aggregate functions support counts, averages, sums, minimums, and maximums.
  • Joins allow Criteria queries to navigate entity relationships.
  • Named queries suit stable query structures, while Criteria queries suit dynamic query construction.
  • Transactions, parameter binding, testing, and performance monitoring are essential for reliable query execution.

28. Conclusion

Named queries and Criteria queries are important tools for data access in Hibernate-based applications. Named queries provide reusable query definitions, while Criteria queries provide a flexible way to construct queries dynamically.

Understanding both techniques helps Java developers create maintainable repositories, implement flexible search functionality, build reports, and manage database operations safely. Choosing the right approach for each requirement improves query clarity, application maintainability, and overall data-access design.


Additional Learning Resources

Explore the Advanced Java Training Course to learn more about Hibernate, ORM, and database integration.

Interested in learning more about the course? Register for a Course Demo.

Visit the JustAcademy Official Website to explore additional training programs and learning resources.

Official documentation: Jakarta Persistence Explained and Hibernate ORM User Guide.

Previous Next
whatsapp